#pragma once

#ifndef __MY_EXCAHNGE__
#define __MY_EXCAHNGE__

#include "../mqcommon/message.pb.h"
#include "../mqcommon/helpper.hpp"
#include <google/protobuf/map.h>
#include <iostream>
#include <string>
#include <cassert>
#include <memory>
#include <mutex>
#include <unordered_map>

namespace NS_Exchange
{

    struct Exchange
    {
        using Ptr = std::shared_ptr<Exchange>;
        std::string name;
        NS_Message::ExchangeType type;
        bool durable;
        bool auto_delete;
        google::protobuf::Map<std::string, std::string> args;

        Exchange(const std::string &ename, NS_Message::ExchangeType etype, bool edurable, bool eauto_delete, const google::protobuf::Map<std::string, std::string> &eargs)
            : name(ename), type(etype), durable(edurable), auto_delete(eauto_delete), args(eargs)
        {
        }

        Exchange(){}

        ~Exchange() {}

        // 通过获取持久化参数字符串来初始化持久化参数，格式为key=value&key=value&key=value&...
        void setArgs(const std::string &str_args)
        {
            std::vector<std::string> sub_args;
            NS_Helper::String::split(str_args, "&", sub_args);

            for (auto &str : sub_args)
            {
                size_t pos = str.find("=");
                std::string key = str.substr(0, pos);
                std::string value = str.substr(pos + 1);
                args[key] = value;
            }
        }

        // 以上述格式序列化返回参数
        std::string getArgs()
        {
            std::string ret;
            for (auto &e : args)
            {
                ret += e.first + "=" + e.second + "&";
            }

            return ret;
        }
    };

    using ExchangeMap = std::unordered_map<std::string, Exchange::Ptr>;

    class ExchangePersist
    {
        const std::string create_sql = "create table if not exists exchange(\
                                        name varchar(32) primary key, \
                                        type int, \
                                        auto_delete int, \
                                        args varchar(128));";
        const std::string drop_sql = "drop table exchange;";

    public:
        ExchangePersist(const std::string &path)
            : _helper(path)
        {
            std::string parentpath = NS_Helper::FileHelper::getParentDirectory(path);
            NS_Helper::FileHelper::createDirectory(parentpath);
            assert(_helper.open());
        }

        // 创建交换机数据表
        void createTable()
        {
            assert(_helper.insert(create_sql));
        }
        // 删除交换机数据表
        void deleteTable()
        {
            assert(_helper.erase(drop_sql));
        }
        // 插入交换机数据
        bool insert(Exchange::Ptr ptr)
        {
            std::stringstream ss;
            ss << "insert into exchange values";
            ss << "(" << "'" << ptr->name << "', ";
            ss << ptr->type << ", ";
            ss << ptr->auto_delete << ", ";
            ss << "'" << ptr->getArgs() << "'" << ");";

            if (!_helper.insert(ss.str()))
            {
                DLOG("持久化交换机数据失败，执行语句: %s", ss.str().c_str());
                return false;
            }

            return true;
        }

        // 删除交换机数据
        void erase(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from exchange where name = ";
            ss << "'" << name << "';";
            if (!_helper.insert(ss.str()))
            {
                DLOG("删除持久化交换机数据失败，执行语句: %s", ss.str().c_str());
            }
        }
        // 获取指定交换机数据
        Exchange::Ptr selectOne(const std::string &name)
        {
            std::stringstream ss;
            ss << "select name, type, auto_delete, args from exchange where name = ";
            ss << "'" << name << "';";

            Exchange::Ptr ret = std::make_shared<Exchange>();
            if (!_helper.select(ss.str(), selectOneCallback, ret))
            {
                DLOG("查询持久化交换机数据失败，执行语句: %s", ss.str().c_str());
            }

            return ret;
        }

        // 获取所有持久化交换机数据
        ExchangeMap recovery()
        {
            std::stringstream ss;
            ss << "select name, type, auto_delete, args from exchange;";

            ExchangeMap mapper;
            if (!_helper.select(ss.str(), selectCallback, mapper))
            {
                DLOG("查询持久化交换机数据失败，执行语句: %s", ss.str().c_str());
            }
            DLOG("恢复历史交换机，交换机数量：%d", (int)mapper.size());
            return mapper;
        }

        // 删除所有交换机数据
        void clear()
        {
            std::stringstream ss;
            ss << "delete from exchange";
            if (!_helper.erase(ss.str()))
            {
                DLOG("删除所有持久化交换机数据失败，执行语句: %s", ss.str().c_str());
            }
        }

        ~ExchangePersist() {}

    private:
        static int selectOneCallback(void *arg, int col_num, char **col_data, char **col_name)
        {
            Exchange::Ptr *ptr = static_cast<Exchange::Ptr *>(arg);
            (*ptr)->name = col_data[0];
            (*ptr)->type = static_cast<NS_Message::ExchangeType>(std::stoi(col_data[1]));
            (*ptr)->durable = true;
            (*ptr)->auto_delete = static_cast<bool>(std::stoi(col_data[2]));

            // 防止存入的args是一个空对象
            if (col_data[3])
                (*ptr)->setArgs(col_data[3]);

            return 0;
        }

        static int selectCallback(void *arg, int col_num, char **col_data, char **col_name)
        {
            ExchangeMap *map = static_cast<ExchangeMap *>(arg);
            Exchange::Ptr ptr = std::make_shared<Exchange>();
            ptr->name = col_data[0];
            ptr->type = static_cast<NS_Message::ExchangeType>(std::stoi(col_data[1]));
            ptr->durable = true;
            ptr->auto_delete = static_cast<bool>(std::stoi(col_data[2]));

            // 防止存入的args是一个空对象
            if (col_data[3])
                ptr->setArgs(col_data[3]);

            map->insert(std::make_pair(ptr->name, ptr));

            return 0;
        }

    private:
    NS_Helper::SQLite3Helper _helper;
    };

    class ExchangeManager
    {
    public:
        using Ptr = std::shared_ptr<ExchangeManager>;

        ExchangeManager(const std::string &filename) : _persist(filename)
        {
            _persist.createTable();
            // 将数据库中持久化的所有交换机恢复到内存中
            _exchanges = _persist.recovery();
        }

        // 声明交换机
        bool declareExchange(const std::string &name,
                             NS_Message::ExchangeType type,
                             bool durable, bool auto_delete,
                             const google::protobuf::Map<std::string, std::string> &args)
        {
            std::unique_lock<std::mutex> ul(_mtx);

            DLOG("声明交换机交换机类型：%d", type == NS_Message::ExchangeType::FANOUT);
            // 如果存在，则返回
            if (_exchanges.find(name) != _exchanges.end())
                return true;

            Exchange::Ptr ptr = std::make_shared<Exchange>();
            ptr->name = name;
            ptr->type = type;
            ptr->durable = durable;
            ptr->auto_delete = auto_delete;
            ptr->args = args;
            _exchanges.insert(std::make_pair(name, ptr));

            // 如果是持久化的交换机，则存储到数据库表中
            if (durable)
            {
                return _persist.insert(ptr);
            }
            return true;
        }

        // 删除指定交换机数据
        void deleteExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            // 如果不存在，则返回
            if (_exchanges.find(name) == _exchanges.end())
                return;

            if (_exchanges[name]->durable)
                _persist.erase(name);

            _exchanges.erase(name);
        }
        // 该接口只能供外界使用，因为这里面也会加锁
        bool exists(const std::string &name)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            return _exchanges.find(name) != _exchanges.end();
        }

        Exchange::Ptr selectOne(const std::string &name)
        {
            std::unique_lock<std::mutex> ul(_mtx);
            if (_exchanges.find(name) == _exchanges.end())
                return nullptr;

            return _exchanges[name];
        }

        size_t size()
        {
            std::unique_lock<std::mutex> ul(_mtx);
            return _exchanges.size();
        }

        // 删除所有交换机数据
        void clear()
        {
            std::unique_lock<std::mutex> ul(_mtx);
            _persist.clear();
            _exchanges.clear();
        }

        ~ExchangeManager() {}

    private:
        // 在访问交换机数据时，必定会使用到多线程
        std::mutex _mtx;
        // 对持久化交换机数据进行管理
        ExchangePersist _persist;
        // 对内存中的交换机进行管理
        ExchangeMap _exchanges;
    };

}

#endif